package edu.cicese.alz.swing;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Random;

/**
 * Created by: Eduardo Quintana Contreras
 * Date: 28/06/12
 * Time: 03:29 PM
 */
public class MapPreview extends JPanel {
	private Color markerBackground, markerBorderColor, locationBackground, locationBorderColor, trackColor;
	private int markerTransparency, locationTransparency, trackTransparency, locationSize, trackThickness;

	private int locationInfoType;
	public static final int INCIDENCES = 0;
	public static final int TIME = 1;
	private Font locationInfoFont = new Font("Calibri", Font.BOLD, 12);

	private int layerType;
	public static final int LAYER_MAP = 0;
	public static final int LAYER_SATELLITE = 1;

//	private boolean showMarkers, showLocations, showTracks, showLabels, showGrid, showZoomControl, showMarkerControl;

	private boolean[] show;
	public static final int MARKERS = 0;
	public static final int LOCATIONS = 1;
	public static final int TRACKS = 2;
	public static final int LABELS = 3;
	public static final int GRID = 4;
	public static final int ZOOM_CONTROL = 5;
	public static final int MARKER_CONTROL = 6;
	public static final int LOCATION_INFO = 7;

	public static final int LABEL_DAY = 8;
	public static final int LABEL_HOUR = 9;
	public static final int LABEL_DISTANCE = 10;
	public static final int LABEL_TIME = 11;
	public static final int LABEL_SPEED = 12;

	private int[] transparencies;
	public static final int MARKER = 0;
	public static final int LOCATION = 1;
	public static final int TRACK = 2;

	// Location sizes (radius)
	private int[] locationRadiuses = new int[]{8, 12, 16, 20, 30};

	// Track thicknesses
	private float[] thicknesses = new float[]{1f, 3f, 5f, 7f};

	// Markers demo coordinates
	private int[] xCoordinates = new int[]{120, 119, 122, 120, 121, 80, 80, 115, 141, 165, 190, 191, 188, 250, 320, 375, 374, 375, 373, 375, 375, 377, 375, 379};
	private int[] yCoordinates = new int[]{15,  30,  18,  70,  110, 150,200,180, 165, 185, 220, 215, 218, 230, 270, 290, 320, 350, 380, 410, 440, 470, 471, 475};

	//
	private String[] example = new String[]{"16/01/2012", "09:14:42", "300m", "60s", "5m/s"};
	private String[] sHours;
	private String[] sDistances;
	private String[] sTimes;
	private String[] sSpeeds;

	// Locations demo coordinates
	private int[] xLocationCoordinates = new int[]{120, 190, 377};
	private int[] yLocationCoordinates = new int[]{19, 215, 470};

	// Locations demo info
	private String[][] locationInfos = new String[][]{{"2", "43"}, {"1", "15"}, {"1", "21"}};

	/*public MapPreview(int width, int height) {
		setPreferredSize(new Dimension(width, height));
		setMaximumSize(getPreferredSize());
		setBorder(BorderFactory.createMatteBorder(3, 0, 3, 0, Color.WHITE));
	}*/

	public MapPreview(boolean show[], int layerType,
	                  Color markerBackground, Color markerBorderColor, int markerTransparency,
	                  Color locationBackground, Color locationBorderColor, int locationTransparency, int locationSize,
	                  int locationInfoType,
	                  Color trackColor, int trackThickness, int trackTransparency,
	                  int width, int height) {
		super();
		setAlignmentX(Component.LEFT_ALIGNMENT);
		setPreferredSize(new Dimension(width, height));
		setMaximumSize(getPreferredSize());
		setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1),
				BorderFactory.createLineBorder(Color.WHITE, 4)));


		this.markerTransparency = markerTransparency;
		setMarkerBackground(markerBackground);
		setMarkerBorderColor(markerBorderColor);

		this.locationTransparency = locationTransparency;
		setLocationBorderColor(locationBorderColor);
		setLocationBackground(locationBackground);
		this.locationSize = locationSize;
		this.locationInfoType = locationInfoType;

		this.trackTransparency = trackTransparency;
		setTrackColor(trackColor);
		setTrackThickness(trackThickness);

		this.layerType = layerType;

		this.show = show;

		createSampleData();
	}

	public void setMarkerBackground(Color markerBackground) {
		this.markerBackground = SwingUtil.getTransparentColor(markerBackground, markerTransparency);
	}

	public void setMarkerBorderColor(Color markerBorderColor) {
		this.markerBorderColor = SwingUtil.getTransparentColor(markerBorderColor, markerTransparency);
	}

	public void setLocationBackground(Color locationBackground) {
		this.locationBackground = SwingUtil.getTransparentColor(locationBackground, locationTransparency);
	}

	public void setLocationBorderColor(Color locationBorderColor) {
		this.locationBorderColor = SwingUtil.getTransparentColor(locationBorderColor, locationTransparency);
	}

	public void setTrackColor(Color trackColor) {
		this.trackColor = SwingUtil.getTransparentColor(trackColor, trackTransparency);
	}

	public void setMarkerTransparency(int markerTransparency) {
		this.markerTransparency = markerTransparency;
		setMarkerBackground(markerBackground);
		setMarkerBorderColor(markerBorderColor);
	}

	public void setLocationTransparency(int locationTransparency) {
		this.locationTransparency = locationTransparency;
		setLocationBackground(locationBackground);
		setLocationBorderColor(locationBorderColor);
	}

	public void setTrackTransparency(int trackTransparency) {
		this.trackTransparency = trackTransparency;
		setTrackColor(trackColor);
	}

	public void setTrackThickness(int trackThickness) {
		this.trackThickness = trackThickness - 1;
	}

	public void setLocationInfoType(int locationInfoType) {
		this.locationInfoType = locationInfoType;
	}

	public void setLocationSize(int locationSize) {
		this.locationSize = locationSize;
	}

	public void setShow(int option, boolean show) {
		this.show[option] = show;
	}

	public void setShow(boolean[] show) {
		this.show = show;
	}

	public void setLayerType(int layerType) {
		this.layerType = layerType;
	}

	private void createSampleData() {
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
		int length = xCoordinates.length;
		sHours = new String[length];
		sDistances = new String[length];
		sTimes = new String[length];
		sSpeeds = new String[length];

		Calendar calendar = Calendar.getInstance();
		try {
			calendar.setTime(sdf.parse("09:10:12"));
		} catch (ParseException e) { /*ignored*/ }

		Random random = new Random();
		for (int i = 0; i < length; i++) {
			int randomDistance = random.nextInt(200) + 200;
			sDistances[i] = randomDistance + "m";
			int randomTime = random.nextInt(40) + 40;
			sTimes[i] = randomTime + "s";
			sSpeeds[i] = String.format("%.2f", randomDistance / (float)randomTime) + "m/s";
			calendar.add(Calendar.SECOND, randomTime);
			sHours[i] = sdf.format(calendar.getTime());
		}
	}

	@Override
	public void paintComponent(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;

		// draw background map
		String layer = layerType == LAYER_MAP ? "map_preview_map.png" : "map_preview_satellite.png";
		g2.drawImage(new ImageIcon(getClass().getClassLoader().getResource("images/" + layer)).getImage(), 0, 0, null);

		// draw tracks
		if (show[TRACKS]) {
			g2.setColor(trackColor);
			g2.setStroke(new BasicStroke(thicknesses[trackThickness], BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
			g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));

			for (int i = 0; i < xCoordinates.length; i++) {
				if (i < xCoordinates.length - 1) {
					g2.draw(new Line2D.Float(xCoordinates[i], yCoordinates[i], xCoordinates[i + 1], yCoordinates[i + 1]));
				}
			}
		}


		// draw markers
		if (show[MARKERS]) {
			g2.setStroke(new BasicStroke());
			int radius = 5;
			int diameter = radius * 2;

			for (int i = 0; i < xCoordinates.length; i++) {
				int x = xCoordinates[i];
				int y = yCoordinates[i];
				Area areaOne = new Area(new Ellipse2D.Double(x - radius, y - radius - 8, diameter, diameter));
				Polygon triangle = new Polygon();
				triangle.addPoint(x - 5, y - 7);
				triangle.addPoint(x + 5, y - 7);
				triangle.addPoint(x, y);
				Area areaTwo = new Area(triangle);
				areaOne.add(areaTwo);
				g2.setColor(markerBackground);
				g2.fill(areaOne);
				g2.setColor(markerBorderColor);
				g2.draw(areaOne);
			}
		}

		// draw labels
		if (show[LABELS]) {
			g2.setFont(locationInfoFont);

			for (int i = 0; i < xCoordinates.length; i++) {
				String labelString = "";
				if (show[LABEL_DAY]) {
					labelString += "16/01/2012 - ";
				}
				if (show[LABEL_HOUR]) {
					labelString += sHours[i] + " - ";
				}
				if (show[LABEL_DISTANCE]) {
					labelString += sDistances[i] + " - ";
				}
				if (show[LABEL_TIME]) {
					labelString += sTimes[i] + " - ";
				}
				if (show[LABEL_SPEED]) {
					labelString += sSpeeds[i] + " - ";
				}
				if (!labelString.equals("")) {
					labelString = labelString.substring(0, labelString.length() - 3);
				}
				int x = xCoordinates[i] + 7, y = yCoordinates[i] - 2;
				g2.setColor(Color.WHITE);
				g2.drawString(labelString, ShiftWest(x, 1), ShiftNorth(y, 1));
				g2.drawString(labelString, ShiftWest(x, 1), ShiftSouth(y, 1));
				g2.drawString(labelString, ShiftEast(x, 1), ShiftNorth(y, 1));
				g2.drawString(labelString, ShiftEast(x, 1), ShiftSouth(y, 1));
				g2.setColor(Color.BLACK);
				g2.drawString(labelString, x, y);
			}
		}


		// draw locations
		if (show[LOCATIONS]) {
			g2.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
			g2.setFont(locationInfoFont);
			FontMetrics metrics = g2.getFontMetrics();

			int radius = locationSize;
			int diameter = radius * 2;
			for (int i = 0; i < xLocationCoordinates.length; i++) {
				Area area = new Area(new Ellipse2D.Double(xLocationCoordinates[i] - radius, yLocationCoordinates[i] - radius, diameter, diameter));
				g2.setColor(locationBackground);
				g2.setStroke(new BasicStroke());
				g2.fill(area);
				g2.setColor(locationBorderColor);
				g2.setStroke(new BasicStroke(2));
				g2.draw(area);

				if (show[LOCATION_INFO]) {
					String text = locationInfos[i][locationInfoType];
					int textWidth = metrics.stringWidth(text);
					int textHeight = metrics.getHeight();
					int x = xLocationCoordinates[i] - textWidth / 2;
					int y = yLocationCoordinates[i] + textHeight / 2 - 3;

					g2.setColor(Color.WHITE);
					g2.drawString(text, ShiftWest(x, 1), ShiftNorth(y, 1));
					g2.drawString(text, ShiftWest(x, 1), ShiftSouth(y, 1));
					g2.drawString(text, ShiftEast(x, 1), ShiftNorth(y, 1));
					g2.drawString(text, ShiftEast(x, 1), ShiftSouth(y, 1));
					g2.setColor(Color.BLACK);
					g2.drawString(text, x, y);
				}
			}
		}

		// draw grid
		if (show[GRID]) {
			g2.setColor(Color.BLACK);
			g2.setStroke(new BasicStroke());
			for (int y = 0; y <= 517; y += 100) {
				g2.drawLine(0, y, 400, y);
			}
			for (int x = 0; x <= 400; x += 100) {
				g2.drawLine(x, 0, x, 517);
			}
		}

		// draw zoom control
		if (show[ZOOM_CONTROL]) {
			g2.drawImage(new ImageIcon(getClass().getClassLoader().getResource("images/zoom_control.png")).getImage(), 15, 50, null);
		}

		// draw marker control
		if (show[MARKER_CONTROL]) {
			g2.drawImage(new ImageIcon(getClass().getClassLoader().getResource("images/marker_control.png")).getImage(), 50, 10, null);
		}
	}

	int ShiftNorth(int p, int distance) {
		return (p - distance);
	}

	int ShiftSouth(int p, int distance) {
		return (p + distance);
	}

	int ShiftEast(int p, int distance) {
		return (p + distance);
	}

	int ShiftWest(int p, int distance) {
		return (p - distance);
	}
}
